package cn.zhangfusheng.elasticsearch.util.date;

import cn.zhangfusheng.elasticsearch.constant.Regular;
import cn.zhangfusheng.elasticsearch.util.RegularUtil;
import cn.zhangfusheng.elasticsearch.util.date.enumeration.DateAddEnum;
import cn.zhangfusheng.elasticsearch.util.date.enumeration.DateFromatEnum;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;

/**
 * 是否需要添加 synchronized 关键字
 * @author fusheng.zhang
 */
public final class LocalDateTimeUtils {

    private final static String[] WEEKS = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};

    /**
     * 获取当前时间
     * @return
     */
    public static String nowTime(DateFromatEnum dateFromatEnum) {
        return LocalDateTimeUtils.format(LocalDateTime.now(), dateFromatEnum);
    }

    /**
     * Date转LocalDateTime
     * @param date Date对象
     * @return
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转换为Date
     * @param dateTime LocalDateTime对象
     * @return
     */
    public static Date localDateTimeToDate(LocalDateTime dateTime) {
        return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 格式化时间
     * @param localDateTime  LocalDateTime对象
     * @param dateFromatEnum 格式化表达式
     * @return
     */
    public static String format(LocalDateTime localDateTime, DateFromatEnum dateFromatEnum) {
        return localDateTime.format(dateFromatEnum.getFormatter());
    }

    /**
     * 格式化时间
     * @param localDateTime LocalDateTime对象
     * @param pattern       格式化表达式
     * @return
     */
    public static String format(LocalDateTime localDateTime, String pattern) {
        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化时间
     * @param date    Date
     * @param pattern 格式化表达式
     * @return
     */
    public static String format(Date date, String pattern) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 反序列化
     * 注意
     * @param dateTime       字符串时间
     * @param dateFromatEnum 格式化方式
     * @return
     */
    public static LocalDateTime parse(String dateTime, DateFromatEnum dateFromatEnum) {
        if (DateFromatEnum.YYYY.equals(dateFromatEnum)) {
            return Year.parse(dateTime, dateFromatEnum.getFormatter()).atDay(1).atStartOfDay();
        } else if (DateFromatEnum.YYYY_MM.equals(dateFromatEnum) || DateFromatEnum.YYYY_MM_1.equals(dateFromatEnum) || DateFromatEnum.YYYY_MM_2.equals(dateFromatEnum)) {
            return YearMonth.parse(dateTime, dateFromatEnum.getFormatter()).atDay(1).atStartOfDay();
        } else if (DateFromatEnum.YYYY_MM_DD.equals(dateFromatEnum) || DateFromatEnum.YYYY_MM_DD_1.equals(dateFromatEnum) || DateFromatEnum.YYYY_MM_DD_2.equals(dateFromatEnum)) {
            return LocalDate.parse(dateTime, dateFromatEnum.getFormatter()).atStartOfDay();
        } else {
            return LocalDateTime.parse(dateTime, dateFromatEnum.getFormatter());
        }
    }

    /**
     * 通过匹配正则表达式反序列化时间
     * @param dateTime
     * @return
     */
    public static LocalDateTime parseMatches(String dateTime) {
        if (RegularUtil.matches(dateTime, Regular.PATTERN_TIME_YYYY)) {
            return parse(dateTime, DateFromatEnum.YYYY);
        } else if (RegularUtil.matches(dateTime, Regular.PATTERN_TIME_YYYY_MM)) {
            return parse(dateTime, DateFromatEnum.YYYY_MM);
        } else if (RegularUtil.matches(dateTime, Regular.PATTERN_TIME_YYYY_MM_DD)) {
            return parse(dateTime, DateFromatEnum.YYYY_MM_DD);
        } else if (RegularUtil.matches(dateTime, Regular.PATTERN_TIME_UUUU_MM_DD_T_HH_MM_SS)) {
            return parse(dateTime, DateFromatEnum.UUUU_MM_DD_T_HH_MM_SS);
        } else if (RegularUtil.matches(dateTime, Regular.PATTERN_TIME_UUUU_MM_DD_T_HH_MM_SS_Z)) {
            return parse(dateTime, DateFromatEnum.UUUU_MM_DD_T_HH_MM_SS_Z);
        } else {
            return parse(dateTime, DateFromatEnum.YYYY_MM_DD_HH_MM_SS);
        }
    }

    /**
     * 获取当天的开始时间
     * @param localDateTime 时间
     * @return
     */
    public static LocalDateTime getStartTime(LocalDateTime localDateTime) {
        return localDateTime.toLocalDate().atStartOfDay();
    }

    /**
     * 日期计算
     * 根据时间单位计算
     * plus('2021-01-01 11:11:11',1,DateAddEnum.YEARS)  年+1  返回值为 2022-01-01 11:11:11
     * @param localDateTime 时间
     * @param amountToAdd   增值 + -
     * @param dateAddEnum   时间单位 具体参考枚举 ChronoUnit
     * @return
     */
    public static LocalDateTime plus(LocalDateTime localDateTime, long amountToAdd, DateAddEnum dateAddEnum) {
        return localDateTime.plus(amountToAdd, dateAddEnum.getChronoUnit());
    }

    /**
     * 获取周几
     * @param localDateTime
     * @return
     */
    public static String getWeek(LocalDateTime localDateTime) {
        return WEEKS[localDateTime.getDayOfWeek().getValue()];
    }

    /**
     * 获取本月有多少天
     * @param localDateTime
     * @return
     */
    public static int getMonthDays(LocalDateTime localDateTime) {
        return localDateTime.getMonth().length(localDateTime.toLocalDate().isLeapYear());
    }

    /**
     * 判断今年是否是闰年
     * @param localDateTime
     * @return
     */
    public static boolean isLeapYear(LocalDateTime localDateTime) {
        return localDateTime.toLocalDate().isLeapYear();
    }

    /**
     * 获取某天的开始时间00:00:00
     * @param dateTime 某天时间
     * @return
     */
    public static LocalDateTime getDayStart(LocalDateTime dateTime) {
        return dateTime.with(LocalTime.MIN);
    }

    /**
     * 获取某天的结束时间23:59:59
     * @param dateTime
     * @return
     */
    public static LocalDateTime getDayEnd(LocalDateTime dateTime) {
        return dateTime.with(LocalTime.MAX);
    }

    /**
     * 获取某月第一天的00:00:00
     * @param dateTime LocalDateTime对象
     * @return
     */
    public static LocalDateTime getFirstDayOfMonth(LocalDateTime dateTime) {
        return dateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
    }

    /**
     * 获取某月最后一天的23:59:59
     * @param dateTime LocalDateTime对象
     * @return
     */
    public static LocalDateTime getLastDayOfMonth(LocalDateTime dateTime) {
        return dateTime.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
    }

    /**
     * 获取某个时间点的上几个月的第一天
     * 例如 获取 2021-12-13 15:23:23 两个月前的一号,2021-10-01 00:00:00
     * @param dateTime      指定时间点
     * @param previousMonth 正数:计算前几个月(当前月数-previousMonth),负数:计算后几个月(当前月数+previousMonth)
     * @return
     */
    public static LocalDateTime getFirstDayOfPreviousMonth(LocalDateTime dateTime, int previousMonth) {
        return dateTime.minusMonths(previousMonth).with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
    }

    /**
     * 获取某个时间点的上几个月的最一天
     * 例如 获取 2021-12-13 15:23:23 两个月后的最后一天,2022-02-28 23:59:59
     * @param dateTime
     * @param previousMonth 正数:计算后几个月(当前月数-previousMonth),负数:计算后几个月(当前月数+previousMonth)
     * @return
     */
    public static LocalDateTime getLastDayOfPreviousMonth(LocalDateTime dateTime, int previousMonth) {
        return dateTime.minusMonths(previousMonth).with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
    }

    /**
     * 获取时间戳
     * @param isMilli true 毫秒级时间戳 false 秒级时间戳
     * @return
     */
    public static Long getTimeStamp(boolean isMilli) {
        return isMilli ?
                LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()
                : LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
    }

    /**
     * 获取指定时间时间戳
     * @param localDateTime 指定时间
     * @param isMilli       true 毫秒级时间戳 false 秒级时间戳
     * @return
     */
    public static long getTimeStamp(LocalDateTime localDateTime, boolean isMilli) {
        return isMilli ?
                localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()
                : localDateTime.toEpochSecond(ZoneOffset.of("+8"));
    }

    /**
     * 获取周一的开始时间
     * @param localDate
     * @return
     */
    public static LocalDate getFirtWeek(LocalDate localDate) {
        return localDate.with(DayOfWeek.MONDAY).with(LocalTime.MIN);
    }

    /**
     * 获取周一的开始时间
     * @param dateTime
     * @return
     */
    public static LocalDateTime getFirtWeek(LocalDateTime dateTime) {
        return dateTime.with(DayOfWeek.MONDAY).with(LocalTime.MIN);
    }

    /**
     * 获取 [上/下]第几周的周一的开始时间
     * @param dateTime     指定时间
     * @param previousWeek 正数:获取[前]第几周,负数:计算[后]第几周
     * @return
     */
    public static LocalDateTime getPreviousFirstWeek(LocalDateTime dateTime, long previousWeek) {
        return dateTime.minusWeeks(previousWeek).with(DayOfWeek.MONDAY).with(LocalTime.MIN);
    }

    /**
     * 获取一周的周日的结束时间
     * @param localDate
     * @return
     */
    public static LocalDate getEndWeek(LocalDate localDate) {
        return localDate.with(DayOfWeek.SUNDAY).with(LocalTime.MAX);
    }

    /**
     * 获取一周的周日的结束时间
     * @param localDateTime
     * @return
     */
    public static LocalDateTime getEndWeek(LocalDateTime localDateTime) {
        return localDateTime.with(DayOfWeek.SUNDAY).with(LocalTime.MAX);
    }

    /**
     * 获取[前/后]第几周的周日的结束时间
     * @param dateTime     指定时间
     * @param previousWeek 正数:获取[前]第几周,负数:计算[后]第几周
     * @return
     */
    public static LocalDateTime getPreviousEndWeek(LocalDateTime dateTime, long previousWeek) {
        return dateTime.minusWeeks(previousWeek).with(DayOfWeek.SUNDAY).with(LocalTime.MAX);
    }

    /**
     * 比较
     * @param start          开始时间
     * @param end            结束时间
     * @param dateFromatEnum 格式化
     * @return -1:start < end 0:start == end  >=1: start > end
     */
    public static int compareTo(String start, String end, DateFromatEnum dateFromatEnum) {
        LocalDateTime startDate = parse(start, dateFromatEnum);
        LocalDateTime endDate = parse(end, dateFromatEnum);
        return startDate.compareTo(endDate);
    }

    /**
     * 获取指定时间的整点时间
     * @param localDateTime
     * @param dateFromatEnum
     * @return
     */
    public static LocalDateTime getOnTheHour(LocalDateTime localDateTime, DateFromatEnum dateFromatEnum) {
        String format = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00:00"));
        return parse(format, dateFromatEnum);
    }

    /**
     * 获取一年的开始时间
     * @return
     */
    public static LocalDateTime getYearStart() {
        LocalDateTime localDateTime = LocalDateTime.now();
        return localDateTime.withYear(localDateTime.getYear())
                .withMonth(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
    }

    /**
     * 获取指定年的开始时间
     * @param year
     * @return
     */
    public static LocalDateTime getYearStart(Integer year) {
        return LocalDateTime.now().withYear(year)
                .withMonth(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
    }

    /**
     * 获取某年某月的开始日期
     * @param year  某年
     * @param month 某月
     * @return
     */
    public static LocalDateTime getMonthStart(Integer year, Integer month) {
        return LocalDateTime.now().withYear(year)
                .withMonth(month).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
    }

    /**
     * 获取小时开始时间
     * @param localDateTime
     * @return
     */
    public static LocalDateTime getHourStart(LocalDateTime localDateTime) {
        return localDateTime.withMinute(0).withSecond(0);
    }
}